//
// Created by xunanmu on 2021/2/16.
//

#include "hjson.h"
#include <assert.h>  /* assert() */
#include <stdlib.h>  /* NULL */

/**
 * n ➔ null
 * t ➔ true
 * f ➔ false
 * " ➔ string
 * 0-9/- ➔ number
 * [ ➔ array
 * { ➔ object
 **/
#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0)

typedef struct {
    const char* json;
}HjsonContext;

static void hjson_parse_whitespace(HjsonContext* c) {
    const char *p = c->json;
    while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
        p++;
    c->json = p;
}

static int hjson_parse_null(HjsonContext* c, HjsonValue* v) {
    EXPECT(c, 'n');
    if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')
        return HJSON_PARSE_INVALID_VALUE;
    c->json += 3;
    v->type = HJSON_NULL;
    return HJSON_PARSE_OK;
}

static int hjson_parse_true(HjsonContext* c, HjsonValue* v){
    EXPECT(c,'t');
    if (c->json[0] != 'r' || c->json[1] !='u' ||c->json[2] != 'e')
        return HJSON_PARSE_INVALID_VALUE;
    c->json += 3;
    v->type = HJSON_TRUE;
    return HJSON_PARSE_OK;
}

static int hjson_parse_false(HjsonContext* c, HjsonValue* v){
    EXPECT(c,'f');
    if (c->json[0] != 'a' || c->json[1] !='l' ||c->json[2] != 's' || c->json[3] != 'e')
        return HJSON_PARSE_INVALID_VALUE;
    c->json += 4;
    v->type = HJSON_FALSE;
    return HJSON_PARSE_OK;
}

static int hjson_parse_number(HjsonContext* c,HjsonValue* v){
    char* end;
    /* \TODO validate number */
    v->number = strtod(c->json, &end);
    if (c->json == end)
        return HJSON_PARSE_INVALID_VALUE;
    c->json = end;
    v->type = HJSON_NUMBER;
    return HJSON_PARSE_OK;
}

static int hjson_parse_value(HjsonContext* c,HjsonValue* v) {
    switch (*c->json) {
        case 'n': return hjson_parse_null(c,v);
        case 't': return hjson_parse_true(c,v);
        case 'f': return hjson_parse_false(c,v);
        case '\0': return HJSON_PARSE_EXPECT_VALUE;
        default: return HJSON_PARSE_INVALID_VALUE;
    }
}

int hjson_parse(HjsonValue* hjsonValue,const char* json ){
    HjsonContext hjsonContext;
    assert(hjsonValue != NULL);
    hjsonContext.json = json;
    hjsonValue->type = HJSON_NULL;
    hjson_parse_whitespace(&hjsonContext);
    int ret;
    if ((ret = hjson_parse_value(&hjsonContext, hjsonValue)) == HJSON_PARSE_OK) {
        hjson_parse_whitespace(&hjsonContext);
        if (*hjsonContext.json != '\0')
            ret = HJSON_PARSE_ROOT_NOT_SINGULAR;
    }
    return ret;
}

HjsonType hjson_get_type(const HjsonValue* hjsonValue){
    assert(hjsonValue != NULL);
    return hjsonValue->type;
}

double hjson_get_number(const HjsonValue *hjsonValue){
    assert(hjsonValue != NULL && hjsonValue->type == HJSON_NUMBER);
    return hjsonValue->number;
}
